package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"github.com/boltdb/bolt"
	"os"
	"strconv"
)

/**
 *@author:zhengyilong
 *@email:1797344574@qq.com
 *@phone:13479394730
 *@DateTime:2022/4/14 19:03
 **/

const (
	DB_PASH = "./bank.db"			//要保存的文件地址
	BUCKET_NAME_USERS = "users"		//key值为用户名，value值为密码
	BUCKET_NAME_MANEY = "maney"		//key值为用户名，value值为余额
	BUCKET_NAME_STATUS = "status"	//key值固定为nowUser,value值为当前登录的用户，如果为空表示没有用户登录
	STATUS_NOWUSER = "nowuser"		//status桶的固定key值
)

/*
1、注册：register --u 用户名 --p 密码
2、登录：login --u 用户名 --p 密码
3、退出：exit
4、查看当前登录用户：show
5、存钱：in --m 金额
6、取钱：out --m 金额
7、查看余额：view
8、系统功能说明文档：help
 */
func main() {
	switch os.Args[1] {
	case "register":
		register()
	case "login":
		login()
	case "exit":
		exit()
	case "show":
		show()
	case "in":
		in()
	case "out":
		out()
	case "view":
		view()
	case "help":
		help()
	default:
		fmt.Println("暂不支持该功能")
	}
}

//注册
func register() {
	set := flag.NewFlagSet("register", flag.ExitOnError)
	u := set.String("u", "", "账户名")
	p := set.String("p", "", "密码")
	set.Parse(os.Args[2:])

	db, err := bolt.Open(DB_PASH, 0600, nil)
	defer db.Close()
	if err != nil {
		fmt.Println("DB打开失败")
		return
	}

	err = db.Update(func(tx *bolt.Tx) error {
		//使用状态桶
		bk_status := tx.Bucket([]byte(BUCKET_NAME_STATUS))
		//只有状态桶存在，才需要判断当前是否有用户登录
		if bk_status != nil {
			//对当前是否有用户登录进行判断，若有，不可进行注册操作，需退出当前用户才可进行注册操作。
			get := bk_status.Get([]byte(STATUS_NOWUSER))
			if !bytes.Equal(get,[]byte("")) {
				return errors.New("当前为登录状态，不可进行注册操作")
			}
		}

		//使用用户信息桶
		bk_users := tx.Bucket([]byte(BUCKET_NAME_USERS))
		//如果用户信息桶不存在，便创建
		if bk_users == nil {
			bk_users, err = tx.CreateBucket([]byte(BUCKET_NAME_USERS))
			if err != nil {
				return err
			}
		}else {
			//桶存在，查询该用户是否已存在
			get_user := bk_users.Get([]byte(*u))
			//用户已存在，直接返回
			if get_user != nil{
				return errors.New("用户已存在")
			}
		}
		//用户不存在，新建账户
		err = bk_users.Put([]byte(*u), []byte(*p))
		if err != nil {
			return errors.New("新建账户失败"+err.Error())
		}

		//使用用户余额桶
		bk_maney := tx.Bucket([]byte(BUCKET_NAME_MANEY))
		//如果余额桶不存在，便创建
		if bk_maney == nil {
			bk_maney, err = tx.CreateBucket([]byte(BUCKET_NAME_MANEY))
			if err != nil {
				return err
			}
		}
		//创建一个账户对应的余额，余额为零
		err = bk_maney.Put([]byte(*u), []byte("0"))
		if err != nil {
			return err
		}

		return err
	})

	if err != nil {
		fmt.Println("注册失败："+err.Error())
		return
	}
	fmt.Println("注册成功")
}

//登录
func login() {
	set := flag.NewFlagSet("login", flag.ExitOnError)
	u := set.String("u", "", "账户名")
	p := set.String("p", "", "密码")
	set.Parse(os.Args[2:])

	db, err := bolt.Open(DB_PASH, 0600, nil)
	defer db.Close()
	if err != nil {
		fmt.Println("DB打开失败")
		return
	}

	err = db.Update(func(tx *bolt.Tx) error {
		//使用状态桶
		bk_status := tx.Bucket([]byte(BUCKET_NAME_STATUS))
		//桶不存在，创建桶
		if bk_status == nil {
			bk_status, err = tx.CreateBucket([]byte(BUCKET_NAME_STATUS))
			if err != nil {
				return err
			}
		}
		//对当前是否有用户登录进行判断，若有，不可重复登录，需退出当前用户才可登录其他用户。无需对新建桶后的空值进行判断，因为如果为空那肯定没有用户登录。
		get := bk_status.Get([]byte(STATUS_NOWUSER))
		if !bytes.Equal(get,[]byte("")) {
			return errors.New("当前为登录状态，不可重复登录")
		}

		//使用用户桶
		bk_users := tx.Bucket([]byte(BUCKET_NAME_USERS))
		//如果桶不存在，则登录失败
		if bk_users == nil {
			return errors.New("用户不存在")
		}
		//桶存在，通过用户名获取密码
		password := bk_users.Get([]byte(*u))
		//如果获取为空，则证明用户不存在
		if password == nil {
			return errors.New("用户不存在")
		}
		//将获取到的密码与用户输入密码相比较
		if !bytes.Equal([]byte(*p), password) {
			return errors.New("密码错误")
		}

		//将登录的用户名放入状态桶中
		err = bk_status.Put([]byte(STATUS_NOWUSER), []byte(*u))
		if err != nil {
			return err
		}

		return err
	})

	if err != nil {
		fmt.Println("登录失败："+err.Error())
		return
	}
	fmt.Println("登录成功")
}

//退出
func exit() {
	db, err := bolt.Open(DB_PASH, 0600, nil)
	defer db.Close()
	if err != nil {
		fmt.Println("DB打开失败")
		return
	}

	//使用状态桶
	err = db.Update(func(tx *bolt.Tx) error {
		//状态桶若不存在，直接返回无用户登录
		bk_status := tx.Bucket([]byte(BUCKET_NAME_STATUS))
		if bk_status == nil {
			return errors.New("无用户登录")
		}
		//查看当前登录用户，若为空值返回，返回无用户登录
		get := bk_status.Get([]byte(STATUS_NOWUSER))
		if bytes.Equal(get,[]byte("")) {
			return errors.New("无用户登录")
		}
		//将当前登录用户改为空值
		err = bk_status.Put([]byte(STATUS_NOWUSER), []byte(""))
		if err != nil {
			return err
		}
		return err
	})

	if err != nil {
		fmt.Println("退出失败："+err.Error())
		return
	}
	fmt.Println("退出成功")
}

//查看登录用户
func show() {
	db, err := bolt.Open(DB_PASH, 0600, nil)
	defer db.Close()
	if err != nil {
		fmt.Println("DB打开失败")
		return
	}

	err = db.View(func(tx *bolt.Tx) error {
		//使用状态桶
		bk_status := tx.Bucket([]byte(BUCKET_NAME_STATUS))
		//如果桶不存在，证明无用户登录
		if bk_status == nil {
			return errors.New("无用户登录")
		}
		//如果value值为空，证明无用户登录
		get := bk_status.Get([]byte(STATUS_NOWUSER))
		if bytes.Equal(get, []byte("")) {
			return errors.New("无用户登录")
		}
		//将value值，即当前登录的用户名输出
		fmt.Printf("当前登录的用户为：%s\n",get)
		return err
	})

	if err != nil {
		fmt.Println("查看失败："+err.Error())
		return
	}
}

//存钱
func in() {
	set := flag.NewFlagSet("in", flag.ExitOnError)
	m := set.String("m", "", "金额")
	set.Parse(os.Args[2:])

	db, err := bolt.Open(DB_PASH, 0600, nil)
	defer db.Close()
	if err != nil {
		fmt.Println("DB打开失败")
		return
	}

	err = db.Update(func(tx *bolt.Tx) error {
		//判断当前登录用户
		bk_status := tx.Bucket([]byte(BUCKET_NAME_STATUS))
		if bk_status == nil {
			return errors.New("无用户登录")
		}
		user := bk_status.Get([]byte(STATUS_NOWUSER))
		if bytes.Equal(user,[]byte("")) {
			return errors.New("无用户登录")
		}

		//使用余额桶，查询当前登录用户user的余额
		bk_maney := tx.Bucket([]byte(BUCKET_NAME_MANEY))
		maney := bk_maney.Get(user)
		//将查询到的余额与用户输入的余额相加
		a, _ := strconv.Atoi(*m)
		b, _ := strconv.Atoi(string(maney))
		sum := strconv.FormatInt(int64(a+b), 10)
		//将相加后的余额sum存入余额桶中
		err = bk_maney.Put(user, []byte(sum))
		if err != nil {
			return err
		}
		return err
	})

	if err != nil {
		fmt.Println("存入失败："+err.Error())
		return
	}
	fmt.Println("存入成功")
}

//取钱
func out() {
	set := flag.NewFlagSet("out", flag.ExitOnError)
	m := set.String("m", "", "金额")
	set.Parse(os.Args[2:])

	db, err := bolt.Open(DB_PASH, 0600, nil)
	defer db.Close()
	if err != nil {
		fmt.Println("DB打开失败")
		return
	}

	err = db.Update(func(tx *bolt.Tx) error {
		//判断当前登录用户
		bk_status := tx.Bucket([]byte(BUCKET_NAME_STATUS))
		if bk_status == nil {
			return errors.New("无用户登录")
		}
		user := bk_status.Get([]byte(STATUS_NOWUSER))
		if bytes.Equal(user, []byte("")) {
			return errors.New("无用户登录")
		}

		//使用余额桶，查询当前登录用户user的余额
		bk_maney := tx.Bucket([]byte(BUCKET_NAME_MANEY))
		maney := bk_maney.Get(user)
		//将查询到的余额与用户输入的余额相减
		a, _ := strconv.Atoi(*m)
		b, _ := strconv.Atoi(string(maney))
		//对用户输入的金额与余额对比，判断是否有这么多钱
		if a >= b {
			return errors.New("余额不足")
		}
		sum := strconv.FormatInt(int64(b-a), 10)
		//将相加后的余额sum存入余额桶中
		err = bk_maney.Put(user, []byte(sum))
		if err != nil {
			return err
		}
		return err
	})

	if err != nil {
		fmt.Println("取款失败："+err.Error())
		return
	}
	fmt.Println("取款成功")
}

//查看余额
func view()  {
	db, err := bolt.Open(DB_PASH, 0600, nil)
	defer db.Close()
	if err != nil {
		fmt.Println("DB打开失败")
		return
	}

	err = db.Update(func(tx *bolt.Tx) error {
		//判断当前登录用户
		bk_status := tx.Bucket([]byte(BUCKET_NAME_STATUS))
		if bk_status == nil {
			return errors.New("无用户登录")
		}
		user := bk_status.Get([]byte(STATUS_NOWUSER))
		if bytes.Equal(user, []byte("")) {
			return errors.New("无用户登录")
		}

		//使用余额桶，查询当前登录用户user的余额
		bk_maney := tx.Bucket([]byte(BUCKET_NAME_MANEY))
		maney := bk_maney.Get(user)
		//将余额输入打印
		fmt.Printf("余额为：%s\n",maney)
		return err
	})

	if err != nil {
		fmt.Println("查询余额失败："+err.Error())
		return
	}
}

//帮助
func help() {
	fmt.Println("1、注册：register --u 用户名 --p 密码\n2、登录：login --u 用户名 --p 密码\n3、退出：exit\n" +
		"4、查看当前登录用户：show\n5、存钱：in --m 金额\n6、取钱：out --m 金额\n7、查看余额：view\n8、系统功能说明文档：help")
}